﻿namespace UserStories.Models
{
    using System.Collections.Generic;
    using System.Linq;

    public class FakeProjectsService : IProjectsService
    {
        private List<Project> _projects;
        private List<User> _users;
        private List<Sprint> _sprints;
        private List<UserStory> _userStories;
        private static FakeProjectsService instance;

        public static FakeProjectsService Instance()
        {
            if (instance == null)
            {
                instance = new FakeProjectsService();
            }

            return instance;
        }

        public FakeProjectsService()
        {

            _projects = new List<Project>();

            var project = new Project
            {
                Id = 1,
                Name = "project's name",
                Description = "project's description",
                NumberOfReleases = 3,
                WeeksPerSprint = 4
            };
            project.Team = new[]
                               {
                                   new User
                                       {
                                           Id = 1,
                                           Roles = new Dictionary<int, Role> { { project.Id, new Role { Id = 1 } } }
                                       },
                                   new User
                                       {
                                           Id = 2,
                                           Roles = new Dictionary<int, Role> { { project.Id, new Role { Id = 2 } } }
                                       },
                                    new User
                                       {
                                           Id = 3,
                                           Roles = new Dictionary<int, Role> { { project.Id, new Role { Id = 3 } } }
                                       },
                                    new User
                                       {
                                           Id = 4,
                                           Roles = new Dictionary<int, Role> { { project.Id, new Role { Id = 4 } } }
                                       }

                               };

            _projects.Add(project);

            _users = new List<User>
                         {
                             new User { Id = 1, Name = "Daniel Morgan", UserName = "dmorgan", Password = "123", IsSysAdmin = true,                                            
                                 Roles = new Dictionary<int, Role> { { project.Id, new Role { Id = 1 } } }},
                             new User { Id = 2, Name = "Alexander Ujueta", UserName = "aujueta", Password = "123", IsSysAdmin = false,
                                                                  Roles = new Dictionary<int, Role> { { project.Id, new Role { Id = 2 } } }},
                             new User { Id = 3, Name = "Ricardo Bonilla", UserName = "rbonilla", Password = "123", IsSysAdmin = false,
                                                                  Roles = new Dictionary<int, Role> { { project.Id, new Role { Id = 3 } } }},
                             new User { Id = 4, Name = "Ricardo Roman", UserName = "rroman", Password = "123", IsSysAdmin = false,
                                                                  Roles = new Dictionary<int, Role> { { project.Id, new Role { Id = 4 } } }},
                         };

            _sprints = new List<Sprint>
                           {
                               new Sprint { Id = 1, Release = new Release { Id = 1 }, Project = project },
                               new Sprint { Id = 2, Release = new Release { Id = 2 }, Project = project }
                           };

            _userStories = new List<UserStory>
                               {
                                   new UserStory
                                       {
                                           Id = 1,
                                           Description = "a user story",
                                           Priority = 1,
                                           Points = new PokerCard { Id = 4 },
                                           Project = project,
                                           Sprint = _sprints[0]
                                       }
                               };

            project.Sprints = _sprints.Where(s => s.Project.Id == project.Id);
            project.UserStories = _userStories.Where(s => s.Project.Id == project.Id);
        }

        public IEnumerable<Project> ProjectsForIndex()
        {
            return _projects;
        }

        public Project ProjectForEdit(int id)
        {
            var project = _projects.FirstOrDefault(p => p.Id == id);

            if (project != null)
            {
                project.UserStories = _userStories.Where(u => u.Project.Id == id);
                project.Sprints = _sprints.Where(s => s.Project.Id == id);
            }

            return project;
        }

        public IEnumerable<Role> Roles()
        {
            return new[] { Role.Developer, Role.Tester, Role.ScrumMaster, Role.ProductOwner };
        }

        public IEnumerable<User> Users()
        {
            return _users;
        }

        public User User(string username)
        {
            var user = _users.FirstOrDefault(u => u.UserName.ToUpperInvariant() == username.ToUpperInvariant());

            return user;
        }

        public IEnumerable<PokerCard> PokerCards()
        {
            return new[]
                       {
                           new PokerCard { Id = 1, Name = "Ace" }, 
                           new PokerCard { Id = 2, Name = "One" },
                           new PokerCard { Id = 3, Name = "Two" }, 
                           new PokerCard { Id = 4, Name = "Three" },
                           new PokerCard { Id = 5, Name = "Five" }, 
                           new PokerCard { Id = 6, Name = "Eight" }
                       };
        }

        public void Save(Project project)
        {
            var previous = _projects.FirstOrDefault(p => p.Id == project.Id);

            if (previous != null)
            {
                _projects.Remove(previous);
            }
            else if (project.Id == 0)
            {
                project.Id = (_projects.Any() ? _projects.Max(p => p.Id) : 0) + 1;
            }

            _projects.Add(project);

            //deletes users stories that were removed from the project
            var projectStories = _userStories.Where(u => u.Project.Id == project.Id).ToList();
            foreach (var userStory in projectStories)
            {
                if (project.UserStories.All(u => u.Id != userStory.Id))
                {
                    _userStories.Remove(userStory);
                }

            }

            Save(project.UserStories);

            //deletes sprints that were removed from the project
            var sprints = _sprints.Where(s => s.Project.Id == project.Id).ToList();
            foreach (var sprint in sprints)
            {
                if (project.Sprints.All(s => s.Id != sprint.Id))
                {
                    _sprints.Remove(sprint);
                }
            }

            Save(project.Sprints);
        }

        public void Save(User user)
        {
            user.Id = (_users.Any() ? _users.Max(u => u.Id) : 0) + 1;

            _users.Add(user);
        }

        public void Save(Sprint sprint)
        {
            this.BasicSave(sprint);

            //deletes sprint from stories that were removed from the sprint
            foreach (var userStory in _userStories.Where(u => u.Sprint != null && u.Sprint.Id == sprint.Id))
            {
                if (sprint.UserStories.All(u => u.Id != userStory.Id))
                {
                    userStory.Sprint = null;
                }
            }

            //adds stories to the sprint
            foreach (var userStory in sprint.UserStories)
            {
                var story = _userStories.FirstOrDefault(u => u.Id == userStory.Id);
                story.Sprint = sprint;
            }
        }

        private void BasicSave(Sprint sprint)
        {
            var previous = _sprints.FirstOrDefault(s => s.Id == sprint.Id);

            if (previous != null)
            {
                // only edited data
                previous.Review = sprint.Review;
            }
            if (sprint.Id == 0)
            {
                sprint.Id = (_sprints.Any() ? _sprints.Max(s => s.Id) : 0) + 1;
                sprint.Project = _projects.First(p => p.Id == sprint.Project.Id);
                _sprints.Add(sprint);
            }
        }

        public IEnumerable<UserStory> UsersStoriesForProject(int projectId)
        {
            return _userStories.Where(u => u.Project.Id == projectId);
        }

        public bool IsValidUser(string userName, string password)
        {
            return
                _users.Any(
                    u =>
                    u.UserName.ToUpperInvariant() == userName.ToUpperInvariant()
                    && u.Password.ToUpperInvariant() == password.ToUpperInvariant());
        }

        public void SaveAsSysAdmin(Project project)
        {
            var previous = _projects.FirstOrDefault(p => p.Id == project.Id);

            if (previous != null)
            {
                previous.Name = project.Name;
                previous.Description = project.Description;
                previous.Team = project.Team;
            }
            else if (project.Id == 0)
            {
                project.Id = (_projects.Any() ? _projects.Max(p => p.Id) : 0) + 1;
                _projects.Add(project);
            }

            // adds roles
            foreach (var user in project.Team)
            {
                var storedUser = _users.First(u => u.Id == user.Id);
                storedUser.Roles.Add(project.Id, user.RoleInProject(project));
            }
        }

        public void SaveAsProductOwner(Project project)
        {
            var previous = _projects.FirstOrDefault(p => p.Id == project.Id);

            // since is  saving as a product owner, the project must exist
            if (previous != null)
            {
                previous.NumberOfReleases = project.NumberOfReleases;

                //deletes users stories that were removed from the project
                var projectStories = _userStories.Where(u => u.Project.Id == project.Id).ToList();
                foreach (var userStory in projectStories)
                {
                    if (project.UserStories.All(u => u.Id != userStory.Id))
                    {
                        _userStories.Remove(userStory);
                    }

                }

                Save(project.UserStories);
            }
        }

        public void SaveAsScrumMaster(Project project)
        {
            var previous = _projects.FirstOrDefault(p => p.Id == project.Id);


            // since is  saving as a scrum master, the project must exist
            if (previous != null)
            {
                previous.WeeksPerSprint = project.WeeksPerSprint;

                //deletes sprints that were removed from the project
                var sprints = _sprints.Where(s => s.Project.Id == project.Id).ToList();
                foreach (var sprint in sprints)
                {
                    if (project.Sprints.All(s => s.Id != sprint.Id))
                    {
                        _sprints.Remove(sprint);
                    }
                }

                Save(project.Sprints);
            }
        }

        public Sprint Sprint(int id)
        {
            var sprint = _sprints.FirstOrDefault(s => s.Id == id);
            
            if (sprint != null)
            {
                sprint.UserStories = _userStories.Where(u => u.Sprint != null && u.Sprint.Id == id);
            }

            return sprint;
        }

        private void Save(IEnumerable<Sprint> sprints)
        {
            foreach (var sprint in sprints)
            {
                BasicSave(sprint);
            }
        }

        private void Save(UserStory userStory)
        {
            var previous = _userStories.FirstOrDefault(s => s.Id == userStory.Id);

            if (previous != null)
            {
                userStory.Sprint = previous.Sprint;
                _userStories.Remove(previous);
            }
            
            if (userStory.Id == 0)
            {
                userStory.Id = (_userStories.Any() ? _userStories.Max(s => s.Id) : 0) + 1;
            }

            userStory.Project = _projects.First(p => p.Id == userStory.Project.Id);
            _userStories.Add(userStory);
        }

        private void Save(IEnumerable<UserStory> userStories)
        {
            foreach (var userStory in userStories)
            {
                Save(userStory);
            }
        }
    }
}